package com.pdfbox.sample.md2pdf;

import org.commonmark.node.*;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.NodeRenderer;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.renderer.html.HtmlWriter;
import org.commonmark.renderer.html.HtmlNodeRendererContext;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.*;
import java.util.*;

/**
 * 支持带锚点的目录写法，但是 WPS 好像不支持
 */
public class MarkdownToPdfWithTOC {

    public static void main(String[] args) throws Exception {
        String aiPdfPath = "E:/PdfBox_Examples/aiMd2Pdf2.pdf";
        String markdown = "# 目录\n" +
                "1. [简介](#section-1)\n" +
                "2. [代码示例](#section-2)\n\n" +
                "## 简介\n" +
                "这里是简介内容...\n\n" +
                "## 代码示例\n" +
                "```java\nSystem.out.println(\"Hello\");\n```";

        // 1. 生成带锚点的HTML
        String html = generateHtmlWithAnchors(markdown);
        System.out.println("html\r\n" + html);

        // 2. 生成PDF
        try (OutputStream os = new FileOutputStream(aiPdfPath)) {
            ITextRenderer renderer = new ITextRenderer();
            String fontPath = "C:/Windows/Fonts/simsun.ttc"; // Windows系统宋体路径
            renderer.getFontResolver().addFont(fontPath, "Identity-H", true);
            renderer.setDocumentFromString(wrapAsXhtml(html));
            renderer.layout();
            renderer.createPDF(os);
        }
    }

    private static String generateHtmlWithAnchors(String markdown) {
        // 自定义渲染器：为标题添加id
        HtmlRenderer renderer = HtmlRenderer.builder()
                .nodeRendererFactory(context -> new CustomHeadingRenderer(context))
                .build();

        Parser parser = Parser.builder().build();
        Node document = parser.parse(markdown);

        // 首先生成目录（需先解析一次获取标题）
        List<HeadingInfo> headings = collectHeadings(document);
        String tocHtml = generateToc(headings);

        // 渲染完整HTML（目录 + 内容）
        return tocHtml + renderer.render(document);
    }

    // 收集标题信息（文本和生成的ID）
    private static List<HeadingInfo> collectHeadings(Node document) {
        List<HeadingInfo> headings = new ArrayList<>();
        document.accept(new AbstractVisitor() {
            @Override
            public void visit(Heading heading) {
                String text = getHeadingText(heading);
                String id = "section-" + (headings.size() + 1);
                headings.add(new HeadingInfo(text, id, heading.getLevel()));
                super.visit(heading);
            }
        });
        return headings;
    }

    // 生成目录HTML
    private static String generateToc(List<HeadingInfo> headings) {
        StringBuilder toc = new StringBuilder("<div class=\"toc\"><ul>");
        for (HeadingInfo h : headings) {
            toc.append("<li><a href=\"#").append(h.id).append("\">")
                    .append(h.text).append("</a></li>");
        }
        return toc.append("</ul></div>").toString();
    }

    // 包装为XHTML（Flying-Saucer要求）
    private static String wrapAsXhtml(String htmlBody) {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" +
                "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" +
                "<head>\n" +
                "    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n" +
                "    <title>中文PDF</title>\n" +
                "    <style>\n" +
                "        body { font-family: SimSun; }\n" +  // 指定中文字体
                "        h1 { color: #2c3e50; }\n" +
                "        pre { background: #f8f8f8; padding: 10px; }\n" +
                "    </style>\n" +
                "</head>\n" +
                "<body>\n" + htmlBody + "\n</body>\n</html>";
    }

    // 自定义标题渲染器（添加id属性）
    static class CustomHeadingRenderer implements NodeRenderer {
        private final HtmlNodeRendererContext context;

        public CustomHeadingRenderer(HtmlNodeRendererContext context) {
            this.context = context;
        }

        @Override
        public Set<Class<? extends Node>> getNodeTypes() {
            return Collections.singleton(Heading.class);
        }

        @Override
        public void render(Node node) {
            Heading heading = (Heading) node;
            String id = "section-" + getHeadingIndex(heading);
            HtmlWriter html = context.getWriter();

            // 1. 输出标题开始标签（带属性）
            html.line();
            html.raw("<h" + heading.getLevel() + " id=\"" + id + "\">");  // 使用raw()直接输出

            // 2. 渲染标题内容
            renderChildren(heading);

            // 3. 输出标题结束标签
            html.raw("</h" + heading.getLevel() + ">");
            html.line();
        }

        private void renderChildren(Node parent) {
            Node node = parent.getFirstChild();
            while (node != null) {
                Node next = node.getNext();
                context.render(node);
                node = next;
            }
        }
    }

    // 辅助类：存储标题信息
    static class HeadingInfo {
        String text;
        String id;
        int level;

        HeadingInfo(String text, String id, int level) {
            this.text = text;
            this.id = id;
            this.level = level;
        }
    }

    // 提取标题文本（简化版）
    private static String getHeadingText(Heading heading) {
        StringBuilder text = new StringBuilder();
        Node node = heading.getFirstChild();
        while (node != null) {
            if (node instanceof Text) {
                text.append(((Text) node).getLiteral());
            }
            node = node.getNext();
        }
        return text.toString();
    }

    // 生成标题索引（示例逻辑）
    private static int getHeadingIndex(Heading heading) {
        // 实际项目可能需要更复杂的索引生成
        return 1; // 简化示例
    }
}